package com.dyrj.base.system.task.service.impl;


import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import com.dyrj.base.system.task.dao.SysTaskMapper;
import com.dyrj.base.system.user.model.SysUser;
import com.dyrj.component.properties.UploadProperties;
import com.dyrj.constant.SysFrameConst;
import com.dyrj.utils.UUIDUtil;
import com.dyrj.vo.PageResultMsg;
import lombok.extern.log4j.Log4j2;
import org.apache.shiro.SecurityUtils;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.stereotype.Service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.dyrj.base.system.task.model.SysTask;
import com.dyrj.base.system.task.service.SysTaskService;
import com.dyrj.vo.ResultMsg;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**   
 * @Title: TaskServiceImpl.java 
 * @Package com.dyrj.base.system.user.service.impl
 * @Description: (任务管理)
 * @author   
 * @date 2019年4月13日 下午11:30:23
 * @version V1.0   
 */
@Service
@Log4j2
public class SysTaskServiceImpl implements SysTaskService {

//    @Resource
//    private SchedulerFactoryBean schedulerFactoryBean;

    @Resource
    private Scheduler scheduler;

    @Resource
	private SysTaskMapper sysTaskMapper;

    @Resource
    private UploadProperties uploadProperties;

    private static String triggerPrefixName = "trigger_";

	@Override
	public PageResultMsg table(SysTask record) {
        SysUser loginUser = (SysUser) SecurityUtils.getSubject().getSession().getAttribute(SysFrameConst.SYSTEM_USER);
		record.setUserid(loginUser.getAccount());
		PageInfo<SysTask> list = PageHelper.startPage(record.getOffset(), record.getLimit()).doSelectPageInfo(() -> sysTaskMapper.selectList(record));
		return new PageResultMsg(SysFrameConst.ResultCode.SUCCESS.CODE, list.getTotal(), list.getList());
	}


	@Override
	public ResultMsg add(SysTask record) {
        SysUser loginUser = (SysUser) SecurityUtils.getSubject().getSession().getAttribute(SysFrameConst.SYSTEM_USER);
        record.setId(UUIDUtil.uuid());
        record.setInsertime(new Date());
        record.setUpdatetime(new Date());
        record.setUserid(loginUser.getAccount());

        if(record.getTriggertime()!=null){
            record.setTriggertime(record.getTriggertime().trim());
        }


        if(record.getTaskstatus()!=null){
            if("on".equals(record.getTaskstatus()) || "true".equals(record.getTaskstatus())){
                record.setTaskstatus("1");
            }else{
                record.setTaskstatus("0");
                //如果任务禁用，触发状态启用的要停用。
                record.setTriggerstatus("0");
            }
        }

        if(record.getTriggerstatus()!=null){
            if("on".equals(record.getTriggerstatus()) || "true".equals(record.getTriggerstatus())){
                record.setTriggerstatus("1");
            }else{
                record.setTriggerstatus("0");
            }
        }

//        if("on".equals(record.getTriggertype()) || "true".equals(record.getTriggertype())){
//            record.setTriggertype("1");
//        }else{
//            record.setTriggertype("0");
//        }
        record.setTriggertype("1");


        try {
            sysTaskMapper.insertSelective(record);


            String taskclass = record.getTaskclass();
            String job_time = record.getTriggertime();
            String job_id = record.getId();
            String taskdesc = record.getTaskdesc();
            String taskparam = record.getTaskparam();
            String triggerstatus = record.getTriggerstatus();
            String triggertype = record.getTriggertype();
            String taskjarfile = record.getTaskjarfile();

            if (taskclass != null && !taskclass.equalsIgnoreCase("")) {
                if(taskjarfile!=null && !"".equals(taskjarfile)){
                    String basePath = uploadProperties.getBasePath();
                    String jarPath = basePath+"/"+taskjarfile;
                    loadJarFile(jarPath,this.getClass().getClassLoader());
                }

//                String softPath = "file:D:\\upjar_demo.jar";
//                URL[] urls = {new URL("file:/D:/upjar_demo.jar")};
//                List<Class<?>> list_class = loadClass(softPath,Thread.currentThread().getContextClassLoader());
//                URLClassLoader classLoader = new URLClassLoader(new URL[]{new URL(softPath)},Thread.currentThread().getContextClassLoader());
//                URLClassLoader classLoader = new URLClassLoader(urls, ClassLoader.getSystemClassLoader());
//                Class<? extends Job> job_class = (Class<? extends Job>)classLoader.loadClass(taskclass);
                Class<? extends Job> job_class = (Class<? extends Job>)Class.forName(taskclass);
                JobDetail jobdetail = JobBuilder.newJob(job_class)
                        .withDescription(taskdesc + job_id)
                        .withIdentity(job_id)
                        .build();
                jobdetail.getJobDataMap().put("taskparam",taskparam);
//                TaskJobManager.sched.addJob(jobdetail, true);
//                if ("0".equals(triggertype)) {
//
//                } else if("1".equals(triggertype)) {
//
//                }
                CronTrigger crontrigger = TriggerBuilder.newTrigger().withIdentity(triggerPrefixName + job_id).withSchedule(CronScheduleBuilder.cronSchedule(job_time).withMisfireHandlingInstructionDoNothing()).build();
                scheduler.scheduleJob(jobdetail,crontrigger);
            }

        } catch (Exception e) {
        	e.printStackTrace();
        }

        return new ResultMsg(SysFrameConst.ResultCode.SUCCESS.CODE, SysFrameConst.ResultCode.SUCCESS.MESSAGE);
	}

	@Override
	public ResultMsg enableALLJob(SysTask record) {
        initTaskJobs();
        return new ResultMsg(SysFrameConst.ResultCode.SUCCESS.CODE, SysFrameConst.ResultCode.SUCCESS.MESSAGE);
	}

	@Override
	public SysTask info(String id) {
		SysTask sysTask = sysTaskMapper.selectByPrimaryKey(id);
		return sysTask;
	}
	
	@Override
	public ResultMsg update(SysTask record) {
		String msg = "";
        SysTask record_db = sysTaskMapper.selectByPrimaryKey(record.getId());

        if(record.getTriggertime()!=null){
            record.setTriggertime(record.getTriggertime().trim());
        }

		if(record.getTaskstatus()!=null){
            if("on".equals(record.getTaskstatus()) || "true".equals(record.getTaskstatus())){
                record.setTaskstatus("1");
            }else{
                record.setTaskstatus("0");
                //如果任务禁用，触发状态启用的要停用。
                record.setTriggerstatus("0");
            }
        }

		if(record.getTriggerstatus()!=null){
		    //如果触发状态为启用，前提任务时启用的
            if("on".equals(record.getTriggerstatus()) || "true".equals(record.getTriggerstatus())){
                //如果当前form中Taskstatus设置启用，或者数据库中Taskstatus是启用状态
                if("1".equals(record.getTaskstatus()) ||"1".equals(record_db.getTaskstatus())){
                    record.setTriggerstatus("1");
                }else{
                    msg="任务处于禁用状态，不能设置触发状态启用！请先设置任务状态后，再设置触发状态！";
                }

            }else{
                record.setTriggerstatus("0");
            }
        }

//        if("on".equals(record.getTriggertype()) || "true".equals(record.getTriggertype())){
//            record.setTriggertype("1");
//        }else{
//            record.setTriggertype("0");
//        }


		sysTaskMapper.updateByPrimaryKeySelective(record);

        //关闭任务
        if("0".equals(record.getTriggerstatus())){
            JobKey jobKey = new JobKey(record.getId());

            try {
                scheduler.deleteJob(jobKey);
            } catch (Exception e) {
            	e.printStackTrace();
            }
        }
        //开启任务
        if("1".equals(record.getTriggerstatus())){
            record_db = sysTaskMapper.selectByPrimaryKey(record.getId());

            String taskclass = record_db.getTaskclass();
            String job_time = record_db.getTriggertime();
            String job_id = record_db.getId();
            String taskdesc = record_db.getTaskdesc();
            String taskparam = record_db.getTaskparam();
            String triggerstatus = record_db.getTriggerstatus();
            String triggertype = record_db.getTriggertype();
            String taskjarfile = record_db.getTaskjarfile();

            try {
                if (taskclass != null && !taskclass.equalsIgnoreCase("")) {

//                    String softPath = "D:\\upjar_demo.jar";
//                    String softPath = "D:\\testjar";
//                    URL[] urls = {new URL("file:/D:/upjar_demo.jar")};
//                    ResourceUtils.getURL("file:/D:/upjar_demo.jar")
//                    this.getClass().getClassLoader()
//                    List<Class<?>> list_class = loadClass(softPath,Thread.currentThread().getContextClassLoader());
//                    List<Class<?>> list_class = loadClass(softPath,ClassLoader.getSystemClassLoader());
//                    List<Class<?>> list_class = loadClass(softPath,this.getClass().getClassLoader());
//                    String jarPath = "D:\\upjar_demo.jar";
//                    loadJarFile(jarPath,this.getClass().getClassLoader());

//                    if(taskjarfile!=null && !"".equals(taskjarfile)){
//                        String basePath = uploadProperties.getBasePath();
//                        String jarPath = basePath+"/"+taskjarfile;
//                        loadJarFile(jarPath,this.getClass().getClassLoader());
//                    }

                    JobDetail jobdetail = JobBuilder.newJob((Class<? extends Job>)Class.forName(taskclass))
                            .withDescription(taskdesc + job_id)
                            .withIdentity(job_id)
                            .build();
                    jobdetail.getJobDataMap().put("taskparam",taskparam);
                    CronTrigger crontrigger = TriggerBuilder.newTrigger().withIdentity(triggerPrefixName + job_id).withSchedule(CronScheduleBuilder.cronSchedule(job_time).withMisfireHandlingInstructionDoNothing()).build();
                    if(scheduler.getJobDetail(jobdetail.getKey())!=null){
                        try {
                            scheduler.deleteJob(jobdetail.getKey());
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    scheduler.scheduleJob(jobdetail, crontrigger);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
		return new ResultMsg(SysFrameConst.ResultCode.SUCCESS.CODE, SysFrameConst.ResultCode.SUCCESS.MESSAGE + msg);
	}
	
	@Override
	public ResultMsg delete(String id) {
        JobKey jobKey = new JobKey(id);
        try {
            scheduler.deleteJob(jobKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
		sysTaskMapper.deleteByPrimaryKey(id);
		return new ResultMsg(SysFrameConst.ResultCode.SUCCESS.CODE, SysFrameConst.ResultCode.SUCCESS.MESSAGE);
	}

    @Override
    public ResultMsg uploadJarFile(MultipartFile jarfile, HttpServletRequest request) {
//        String rootPath = request.getServletContext().getRealPath("/").replace("\\", "/");//上传文件的跟路径
//        log.debug("rootPath:"+rootPath);
        String basePath = uploadProperties.getBasePath();
        log.debug("basePath:"+basePath);
//        final String uploadPath = "uploadjar/" + new SimpleDateFormat("yyyy").format(new Date());//上传的组合路径
        final String uploadPath = "uploadjar";//上传的组合路径
        final String fileName = jarfile.getOriginalFilename(); //上传的文件名
//        String[] suffixs = {"jar","jpg","gif","png","jpeg","txt"};
        String[] suffixs = {"jar"};
        File saveFilePath = new File(basePath + uploadPath);
        File saveFile = new File(basePath + uploadPath,fileName);
        log.debug("saveFilePath:"+saveFilePath);
        log.debug("saveFile:"+saveFile);
        String msg = "";
        if(!saveFilePath.exists())
            saveFilePath.mkdirs();//目录不存在，递归创建
        if(Arrays.asList(suffixs).contains(getSuffix(saveFile.getName())) == false){//上传文件名检查（白名单机制）true:通过，false：没有通过
            return new ResultMsg(SysFrameConst.ResultCode.UPLOAD_FILETYPE_ERROR.CODE, SysFrameConst.ResultCode.UPLOAD_FILETYPE_ERROR.MESSAGE);
        }else{
            try {
                jarfile.transferTo(saveFile);//保存文件
                msg = "文件上传成功";
                Map map_data = new HashMap<String, Object>() {
                    {
                        put("taskjarfile", uploadPath + "/"+ fileName);
                    }
                };

//                String data = JSONObject.toJSONString(map_data);

                return new ResultMsg(SysFrameConst.ResultCode.SUCCESS.CODE, SysFrameConst.ResultCode.SUCCESS.MESSAGE + msg, map_data);

            } catch (Exception e) {
                e.printStackTrace();
                msg = e.getMessage();
            }
        }

        return new ResultMsg(SysFrameConst.ResultCode.SUCCESS.CODE, SysFrameConst.ResultCode.SUCCESS.MESSAGE + msg);
    }

    /**
     * 文件后缀
     * @return
     */
    public String getSuffix(String name) {
        String suffix = name;
        if(suffix.contains("."))
            suffix = suffix.substring(suffix.lastIndexOf(".")+1);
        else
            suffix = "";
        return suffix;
    }


    /**
     * 重新启动所有的job
     */
    public void initTaskJobs() {
        try {
//            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            Set<JobKey> set = scheduler.getJobKeys(GroupMatcher.anyGroup());
            for (JobKey jobKey : set) {
                scheduler.deleteJob(jobKey);
            }

            SysTask query_task = new SysTask();
            query_task.setTaskstatus("1");
            query_task.setTriggerstatus("1");
            List<SysTask> list_systask = sysTaskMapper.selectList(query_task);

            for (SysTask systask : list_systask) {
                try {
                    String taskclass = systask.getTaskclass();
                    String job_time = systask.getTriggertime();
                    String job_id = systask.getId();
                    String taskdesc = systask.getTaskdesc();
                    String taskparam = systask.getTaskparam();
//                    String triggerstatus = systask.getTriggerstatus();
//                    String triggertype = systask.getTriggertype();

                    if (taskclass != null && !taskclass.equalsIgnoreCase("")) {
                        log.info(String.format("Job enable desc:{%s}, classname:{%s}, cron:{%s}", systask.getTaskdesc(), systask.getTaskclass(), systask.getTriggertime()));
                        JobDetail jobdetail = JobBuilder.newJob((Class<? extends Job>)Class.forName(taskclass))
                                .withDescription(taskdesc + job_id)
                                .withIdentity(job_id)
                                .build();
                        jobdetail.getJobDataMap().put("taskparam",taskparam);
                        CronTrigger crontrigger = TriggerBuilder.newTrigger().withIdentity(triggerPrefixName + job_id).withSchedule(CronScheduleBuilder.cronSchedule(job_time)).build();
                        scheduler.scheduleJob(jobdetail,crontrigger);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("error:" + e.getMessage());
                }
            }
        } catch (Exception e) {
            log.error("error:" + e.getMessage());
            e.printStackTrace();
        }
    }

//    /**
//     * 暂停定时任务
//     */
//    @Override
//    public ResultMsg pause(String id) {
//        String msg = "";
//        TriggerKey triggerKey = TriggerKey.triggerKey(triggerPrefixName + id);
//        try {
//            if (scheduler.checkExists(triggerKey)) {
//                scheduler.pauseTrigger(triggerKey);
//                log.info("===> Pause success, triggerKey:{}", triggerKey);
//
//                SysTask record = new SysTask();
//                record.setId(id);
//                record.setTriggerstatus("0");
//                sysTaskMapper.updateByPrimaryKeySelective(record);
//            }
//
//        } catch (SchedulerException e) {
//            e.printStackTrace();
//            msg = e.getMessage();
//        }
//        return new ResultMsg(SysFrameConst.ResultCode.SUCCESS.CODE, SysFrameConst.ResultCode.SUCCESS.MESSAGE + msg);
//    }
//
//    @Override
//    public ResultMsg resume(String id) {
//        String msg = "";
//        TriggerKey triggerKey = TriggerKey.triggerKey(triggerPrefixName + id);
//        try {
//            if (scheduler.checkExists(triggerKey)) {
//                scheduler.resumeTrigger(triggerKey);
//                log.info("===> Resume success, triggerKey:{}", triggerKey);
//
//                SysTask record = new SysTask();
//                record.setId(id);
//                record.setTriggerstatus("1");
//                sysTaskMapper.updateByPrimaryKeySelective(record);
//            }
//
//        } catch (SchedulerException e) {
//            e.printStackTrace();
//            msg = e.getMessage();
//        }
//        return new ResultMsg(SysFrameConst.ResultCode.SUCCESS.CODE, SysFrameConst.ResultCode.SUCCESS.MESSAGE + msg);
//    }

    /**
     * 从本地磁盘的某个路径上加载类， 如果是class文件：
     * filePath路径应该为class文件包名的上一级，如D:\\workspace\\classes\\com\\test\\helloworld.class,那么filePath则应该是D:\\workspace\\classes
     * 如果是jar包：
     * 则是jar包所在目录，如D:\\workspace\\classes\\helloword.jar，那么filePath则应该为D:\\workspace\\classes
     *
     */
    public static void loadJarFile(String filePath, ClassLoader beanClassLoader) {
        File file = new File(filePath);
        URL url = null;
        JarFile jar = null;
        Method method = null;
        Boolean accessible = null;
        String className = file.getAbsolutePath();
        try {
            // 反射并调用URLClassLoader的addURL方法
            URLClassLoader classLoader = (URLClassLoader) beanClassLoader;
            method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
            accessible = method.isAccessible();
            if (accessible == false) {
                method.setAccessible(true);
            }
            // 如果是jar包，加载该jar包
            url = file.toURI().toURL();
            method.invoke(classLoader, url);
            // 获取jar
            jar = new JarFile(new File(className));
            // 从此jar包 得到一个枚举类
            Enumeration<JarEntry> entries = jar.entries();
            // 同样的进行循环迭代
            while (entries.hasMoreElements()) {
                // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
                JarEntry entry = entries.nextElement();
                String name = entry.getName();
                // 如果是以/开头的
                if (name.charAt(0) == '/') {
                    // 获取后面的字符串
                    name = name.substring(1);
                }
                name = name.replace(File.separatorChar, '.');
                // 获取class文件
                if (name.endsWith(".class") && !entry.isDirectory()) {
                    String className1 = name.substring(0, name.length() - 6);
                    classLoader.loadClass(className1);
                    // 添加到classes
//                    classList.add(classLoader.loadClass(className1));
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new RuntimeException(e.getMessage());
        } finally {
            if (null != jar) {
                try {
                    jar.close();
                } catch (IOException e) {
                    log.error(e.getMessage());
                    throw new RuntimeException(e.getMessage());
                }
            }
            if (null != method && null != accessible) {
                method.setAccessible(accessible);
            }
        }

    }

    /**
     * 从本地磁盘的某个路径上加载类， 如果是class文件：
     * filePath路径应该为class文件包名的上一级，如D:\\workspace\\classes\\com\\test\\helloworld.class,那么filePath则应该是D:\\workspace\\classes
     * 如果是jar包：
     * 则是jar包所在目录，如D:\\workspace\\classes\\helloword.jar，那么filePath则应该为D:\\workspace\\classes
     *
     */
    public static List<Class<?>> loadClass(String filePath, ClassLoader beanClassLoader) {
        List<Class<?>> classList = new ArrayList<>();
        File file = new File(filePath);
        if (file.exists() && file.isDirectory()) {

            Stack<File> stack = new Stack<>();
            stack.push(file);
            while (!stack.isEmpty()) {
                File path = stack.pop();
                // 只需要class文件或者jar包
                File[] classFiles = path.listFiles(new FileFilter() {
                    public boolean accept(File pathname) {
                        return pathname.isDirectory() || pathname.getName().endsWith(".class")
                                || pathname.getName().endsWith(".jar");
                    }
                });
                for (File subFile : classFiles) {
                    if (subFile.isDirectory()) {
                        // 如果是目录，则加入栈中
                        stack.push(subFile);
                    } else {
                        URL url = null;
                        JarFile jar = null;
                        Method method = null;
                        Boolean accessible = null;
                        String className = subFile.getAbsolutePath();
                        try {
                            // 反射并调用URLClassLoader的addURL方法
                            URLClassLoader classLoader = (URLClassLoader) beanClassLoader;
                            method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
                            accessible = method.isAccessible();
                            if (accessible == false) {
                                method.setAccessible(true);
                            }

                            if (className.endsWith(".class")) {
                                // 一次性加载目录下的所有class文件 这里一定不要写成url = subFile.toURI().toURL();
                                url = file.toURI().toURL();
                                method.invoke(classLoader, url);
                                // 拼类名，并进行类型加载
                                int clazzPathLen = file.getAbsolutePath().length() + 1;
                                className = className.substring(clazzPathLen, className.length() - 6);
                                className = className.replace(File.separatorChar, '.');
                                classList.add(classLoader.loadClass(className));
                            } else if (className.endsWith(".jar")) {
                                // 如果是jar包，加载该jar包
                                url = subFile.toURI().toURL();
                                method.invoke(classLoader, url);
                                // 获取jar
                                jar = new JarFile(new File(className));
                                // 从此jar包 得到一个枚举类
                                Enumeration<JarEntry> entries = jar.entries();
                                // 同样的进行循环迭代
                                while (entries.hasMoreElements()) {
                                    // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
                                    JarEntry entry = entries.nextElement();
                                    String name = entry.getName();
                                    // 如果是以/开头的
                                    if (name.charAt(0) == '/') {
                                        // 获取后面的字符串
                                        name = name.substring(1);
                                    }
                                    name = name.replace(File.separatorChar, '.');
                                    // 获取class文件
                                    if (name.endsWith(".class") && !entry.isDirectory()) {
                                        String className1 = name.substring(0, name.length() - 6);
                                        // 添加到classes
                                        classList.add(classLoader.loadClass(className1));
                                    }
                                }
                            }
                        } catch (Exception e) {
                            log.error(e.getMessage());
                            throw new RuntimeException(e.getMessage());
                        } finally {
                            if (null != jar) {
                                try {
                                    jar.close();
                                } catch (IOException e) {
                                    log.error(e.getMessage());
                                    throw new RuntimeException(e.getMessage());
                                }
                            }
                            if (null != method && null != accessible) {
                                method.setAccessible(accessible);
                            }
                        }

                    }
                }
            }
        }
        return classList;
    }

}
